﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using FreshMeat.Storage;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Storage.Core.Test
{
   [TestClass]
   public class WildcardSearchEmulatorTest
   {
      // ReSharper disable UnusedMember.Global
      // ReSharper disable UnusedAutoPropertyAccessor.Global
      public TestContext TestContext { get; set; }
      // ReSharper restore UnusedAutoPropertyAccessor.Global
      // ReSharper restore UnusedMember.Global

      [TestMethod]
      public void CaseInsensitive()
      {
         string[] names = {"abc.txt", "ABC.doc", "abc.xls", "AbC.pdf"};
         const string pattern = "abc.*";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(4, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length, "Wrong number of matches.");
      }

      [TestMethod]
      public void OneCharExtMatchesOneCharExt()
      {
         string[] names = {"file.a", "file.ab", "file.abc", "file.abcd", "file.a.abc"};
         const string pattern = "*.a";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(1, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
         Assert.AreEqual("file.a", actual[0]);
      }

      [TestMethod]
      public void TwoCharExtMatchesTwoCharExt()
      {
         string[] names = { "file.a", "file.ab", "file.abc", "file.abcd", "file.a.abc" };
         const string pattern = "*.ab";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(1, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
         Assert.AreEqual("file.ab", actual[0]);
      }

      [TestMethod]
      public void ThreeCharExtWithAsteriskMatchesThreeCharOrMoreExt()
      {
         string[] names = { "file.a", "file.ab", "file.abc", "file.abcd", "file.a.abc" };
         const string pattern = "*.abc";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(3, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
         Assert.AreEqual("file.abc", actual[0]);
         Assert.AreEqual("file.abcd", actual[1]);
         Assert.AreEqual("file.a.abc", actual[2]);
      }

      [TestMethod]
      public void ThreeCharExtWithAsteriskAtEnd()
      {
         string[] names = { "file.a", "file.ab", "file.abc", "file.abcd", "file.a.abc" };
         const string pattern = "file.ab*";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(3, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
         Assert.AreEqual("file.ab", actual[0]);
         Assert.AreEqual("file.abc", actual[1]);
         Assert.AreEqual("file.abcd", actual[2]);
      }

      [TestMethod]
      public void ThreeCharExtWithAsteriskInMiddle()
      {
         string[] names = { "file.a", "file.ab", "file.abc", "file.abcd", "file.apc" };
         const string pattern = "file.a*c";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(2, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
         Assert.AreEqual("file.abc", actual[0]);
         Assert.AreEqual("file.apc", actual[1]);
      }

      [TestMethod]
      public void ThreeCharExtWithoutAsteriskMatchesThreeCharExt()
      {
         string[] names = { "file.a", "file.ab", "file.abc", "file.abcd", "file.a.abc" };
         const string pattern = "????.abc";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(1, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
         Assert.AreEqual("file.abc", actual[0]);
      }

      [TestMethod]
      public void ExactMatchTwoCharExt()
      {
         string[] names = { "file.a", "file.ab", "file.abc", "file.abcd", "file.a.abc" };
         const string pattern = "file.ab";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(1, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
         Assert.AreEqual("file.ab", actual[0]);
      }

      [TestMethod]
      public void ExactMatchThreeCharExt()
      {
         string[] names = { "file.a", "file.ab", "file.abc", "file.abcd", "file.a.abc" };
         const string pattern = "file.abc";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(1, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
         Assert.AreEqual("file.abc", actual[0]);
      }

      [TestMethod]
      public void MultipleDots()
      {
         string[] names = {"file.a.x", "file.ab.xy", "file.abc.xyz", "file.abcd.wxyz", "file.xyz.abc", "blah.abc"};
         const string pattern = "*.abc";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(2, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
         Assert.AreEqual("file.xyz.abc", actual[0]);
         Assert.AreEqual("blah.abc", actual[1]);
      }

      [TestMethod]
      public void AsteriskDotExtMatchesLastExt()
      {
         string[] names = {"file.abc", "file.xyz", "file.abc.xyz", "file.xyz.abc"};
         const string pattern = "*.xyz";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(2, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
         Assert.AreEqual("file.xyz", actual[0]);
         Assert.AreEqual("file.abc.xyz", actual[1]);
      }

      [TestMethod]
      public void AsteriskAloneMatchesEverything()
      {
         string[] names = { "file.a.x", "file.ab.xy", "file.abc.xyz", "file.abcd.wxyz", "file.xyz.abc", "blah.abc" };
         const string pattern = "*";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(6, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
      }

      [TestMethod]
      public void DotAloneMatchesEverything()
      {
         string[] names = { "file.a.x", "file.ab.xy", "file.abc.xyz", "file.abcd.wxyz", "file.xyz.abc", "blah.abc" };
         const string pattern = ".";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(6, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
      }

      [TestMethod]
      public void LeadingSpacesAreSignificant()
      {
         // Note leading spaces in file names (0, 1 and 2)
         string[] names = {"file.abc", " file.abc", "  file.abc"};
         const string pattern = " file.abc"; // note leading space
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(1, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
         Assert.AreEqual(" file.abc", actual[0]);
      }

      [TestMethod]
      public void TrailingSpacesAreSignificant()
      {
         // Windows automatically trims trailing spaces when creating files
         // so the test will have to suffice with just a single file.
         string[] names = { "file" }; 
         const string pattern = "file.abc "; // note trailing space
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(0, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
      }

      [TestMethod]
      public void EmptyPatternMatchesNothing()
      {
         string[] names = { "file.abc", " file.abc", "  file.abc" };
         const string pattern = "";
         var expected = GetRealResults(names, pattern);

         var search = new WildcardSearchEmulator(pattern);
         var actual = names.Where(search.IsMatch).ToArray();

         Assert.AreEqual(0, expected.Length, "Expectations are wrong.");
         Assert.AreEqual(expected.Length, actual.Length);
      }

      //actual = WildcardSearchEmulator("H*o*.???", names);
      //actual = WildcardSearchEmulator("hello.txt", names);
      //actual = WildcardSearchEmulator("lskfjd30", names);

      string[] GetRealResults(IEnumerable<string> names, string pattern)
      {
         var folder = System.IO.Path.Combine(TestContext.TestDir, System.IO.Path.GetRandomFileName());
         Directory.CreateDirectory(folder);

         var count = 0;
         foreach (var name in names)
         {
            var file = System.IO.Path.Combine(folder, name);
            File.WriteAllText(file, "alksjdf laksjdf laksdjflaksdjf laksjdf");
            count++;
         }

         var fileCount = Directory.GetFiles(folder).Count();
         Assert.AreEqual(count, fileCount, "Wrote {0} files but only {1} exist.", count, fileCount);

         var info = new DirectoryInfo(folder);
         return info.GetFiles(pattern).Select(file => file.Name).ToArray();
      }
   }
}
